(function (angular) {
    'use strict';
    angular.module('fs.router').provider('routerService', routerServiceProvider);

    routerServiceProvider.$inject = ['$stateProvider', '$urlRouterProvider', '$locationProvider'];
    function routerServiceProvider($stateProvider, $urlRouterProvider, $locationProvider) {
        var provider = this;
        provider.theme = 'default';
        provider.multipleViews = {};
        provider.cacheLayout = false;
        provider.cacheResource = false;
        provider.cacheAuthorization = false;
        provider.cacheValidationRule = false;
        provider.lazyLoad = lazyLoad;
        provider.preLoadLayoutConfig = preLoadLayoutConfig;
        provider.preLoadValidatorConfig = preLoadValidatorConfig;
        provider.preLoadI18nConfig = preLoadI18nConfig;
        provider.preLoadAuthorizationConfig = preLoadAuthorizationConfig;
        provider.setTheme = function (theme) {
            provider.theme = theme;
        };
        provider.getTheme = function () {
            return provider.theme;
        };
        provider.setHomeUrl = function (homeUrl) {
            $urlRouterProvider.otherwise(homeUrl);
        };
        provider.setStates = function (stateConfig) {
            angular.forEach(stateConfig, function (value, key) {
                if (value.views) {
                    provider.multipleViews[key.split('.').join('_')] = key;
                }
                this.state(key, value);
            }, $stateProvider);
        };
        provider.getTemplateUrl = function (templateUrl) {
            return templateUrl.replace('${theme}', provider.theme);
        };
        provider.setSimpleStates = function (simpleStates, module) {
            var states = {};
            simpleStates = flattenSimpleStateConfig(simpleStates);
            angular.forEach(simpleStates, function (value, key) {
                if (key.indexOf('^') == -1) {
                    simpleStates[module + '_' + key] = value;
                    delete simpleStates[key];
                } else {
                    simpleStates[key.substr(1)] = value;
                    delete simpleStates[key];
                }
            });
            angular.forEach(simpleStates, function (stateConfig, stateName) {
                stateConfig = stateConfig || {};
                if (!this[stateName] && !stateConfig.__ignore) {
                    stateConfig.module = module;
                    this[stateName] = buildState(stateName.split('.').join('_'), stateConfig);
                }
            }, states);
            try {
                provider.setStates(states);
            } catch (e) {
                provider.log.debug(e);
            }
        };

        getter.$inject = ['storageService', 'StorageConstants', '$log'];
        function getter(storageService, StorageConstants, $log) {
            provider.storageService = storageService;
            provider.Constants = StorageConstants;
            provider.log = $log.getInstance('routerService');
            return {
                setTheme: provider.setTheme,
                getTheme: provider.getTheme,
                parseState: parseState
            };
        }
        provider.$get = getter;

        function parseState(state) {
            if (state.charAt(0) == '^') {
                state = state.replace('^', '');
            } else if (provider.multipleViews[state.split('.').join('_')]) {
                state = provider.multipleViews[state.split('.').join('_')];
            } else {
                state = state.split('.').join('_');
            }
            return state;
        }
        function lazyLoad(files) {
            angular.forEach(files, function (file, index) {
                this[index] = file.replace('${theme}', provider.theme);
            }, files);
            return ['$ocLazyLoad', '$stateParams', function ($ocLazyLoad, $stateParams) {
                return $ocLazyLoad.load(files, {
                    cache: true
                });
            }];
        }
        function buildState(stateName, stateConfig, isMultipleViews) {
            var state = {
                url: '/' + (stateConfig.views ? stateName.split('_').pop() : stateName.split('_').join('/')),
                resolve: getResolve(stateName, stateConfig),
                params: {
                    params: stateConfig.params
                }
            };
            if (isMultipleViews) {
                state.controller = stateConfig.controller || getController(stateName, stateConfig.__inheritController);
                state.controllerAs = stateConfig.controllerAs || 'vm';
            } else {
                state.controllerProvider = stateConfig.controllerProvider || getControllerProvider(stateName, stateConfig.__inheritController);
            }
            if (stateConfig.template) {
                state.template = stateConfig.template;
            } else {
                state.templateUrl = getTemplateUrl(stateName, stateConfig.templateUrl, stateConfig.module);
            }
            if (stateConfig.views) {
                state.views = {};
                angular.forEach(stateConfig.views, function (view, key) {
                    state.views[key] = buildState(stateName, view, true);
                    delete state.views[key].url;
                    delete state.views[key].params;
                    delete state.views[key].resolve;
                });
            }
            return state;
        }
        function getResolve(stateName, stateConfig) {
            return {
                dependentFile: preLoadDependFile(stateName, stateConfig.__inheritController, stateConfig.__dependentFiles, stateConfig.module),
                layoutConfig: preLoadLayoutConfig(stateConfig.__layoutConfig, stateName, stateConfig.module),
                validatorConfig: preLoadValidatorConfig(stateConfig.__validatorConfig, stateName, stateConfig.module),
                i18nConfig: preLoadI18nConfig(stateConfig.__i18nConfig, stateName, stateConfig.module),
                authService: preLoadAuthorizationConfig(stateConfig.__authorizationConfig, stateName, stateConfig.module),
            };
        }
        function getTemplateUrl(stateName, templateUrl, module) {
            if (templateUrl) {
                return templateUrl = templateUrl.replace('${theme}', provider.theme);
            }
            return function ($stateParams) {
                var fileName, prefix, path;
                path = stateName.split('_');
                if (path.length > 2) {
                    fileName = path.pop();
                    prefix = path.pop();
                    fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
                    path.push(prefix);
                    path.push(fileName);
                }
                templateUrl = (path.length > 1 ? path.shift() : module) + '/view/' + provider.theme + '/' + path.join('/') + '.html';
                return templateUrl;
            };
        }
        function getController(stateName, inheritController) {
            var controller, stateNameArray;
            stateNameArray = stateName.split('_');
            if (inheritController === true) {
                stateNameArray.pop();
            }
            controller = stateNameArray.join('.') + 'Controller';
            return controller;
        }
        function getControllerProvider(stateName, inheritController) {
            controllerProvider.$inject = ['$state', '$stateParams'];
            function controllerProvider($state, $stateParams) {
                var controller, stateNameArray = stateName.split('_');
                if (inheritController === true) {
                    stateNameArray.pop();
                }
                controller = stateNameArray.join('.') + 'Controller as vm';
                return controller;
            }
            return controllerProvider;
        }
        function preLoadDependFile(stateName, inheritController, files, module) {
            dependentFile.$inject = ['$ocLazyLoad', '$q', '$state', '$log'];
            function dependentFile($ocLazyLoad, $q, $state, $log) {
                var filePathArray, fileName, prefix, defer = $q.defer();
                var dependentFiles = angular.copy(files) || [];
                angular.forEach(dependentFiles, function (filePath, index) {
                    dependentFiles[index] = filePath.replace('${theme}', provider.theme);
                });
                filePathArray = stateName.split('_');
                if (filePathArray.length > 1 && inheritController === true) {
                    filePathArray.pop();
                } else if (filePathArray.length > 2) {
                    fileName = filePathArray.pop();
                    prefix = filePathArray.pop();
                    fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
                    filePathArray.push(prefix);
                    filePathArray.push(fileName);
                }
                dependentFiles.push((filePathArray.length > 1 ? filePathArray.shift() : module) + '/view/' + provider.theme + '/' + filePathArray.join('/') + 'Controller.js');
                $ocLazyLoad.load(dependentFiles, {
                    cache: true
                }).then(function (e) {
                    defer.resolve();
                }, function (e) {
                    defer.resolve();
                });
                return defer.promise;
            }
            return dependentFile;
        }
        function preLoadLayoutConfig(url, stateName, module) {
            layoutConfig.$inject = ['$q', '$stateParams', 'ajaxServiceFactory', 'storageService'];
            function layoutConfig($q, $stateParams, ajaxServiceFactory, storageService) {
                var ajaxService, siteInfo, defer = $q.defer(), layoutUrl;
                if (!url && stateName) {
                    url = stateName.split('_');
                    url = url.slice(-2);
                    url = 'do/' + module + '/layout/' + url.join('/');
                }
                if (url) {
                    siteInfo = storageService.getSiteInfo();
                    layoutUrl = url;
                    if ($stateParams.params && $stateParams.params.siteCode && siteInfo && siteInfo.siteCode && $stateParams.params.siteCode.substr(0, 6) != siteInfo.siteCode) {
                        layoutUrl = layoutUrl + "/" + $stateParams.params.siteCode.substr(0, 6);
                    }
                    ajaxService = ajaxServiceFactory.buildAjaxService({
                        loadConfig: {
                            'url': layoutUrl,
                            'method': 'get',
                            'cache': provider.cachelayout
                        }
                    });
                    ajaxService.loadConfig().then(function (response) {
                        defer.resolve(response ? response.data : {});
                    }, function (error) {
                        provider.log.debug('can not load config[' + layoutUrl + ']:' + error);
                        defer.reject();
                    });
                } else {
                    defer.resolve({});
                }
                return defer.promise;
            }
            return layoutConfig;
        }
        function preLoadValidatorConfig(url, stateName, module) {
            validatorConfig.$inject = ['$rootScope', '$q', 'StorageConstants', 'storageService', 'ajaxServiceFactory'];
            function validatorConfig($rootScope, $q, StorageConstants, storageService, ajaxServiceFactory) {
                var ajaxService, defer = $q.defer();
                if (!url && stateName) {
                    url = stateName.split('_');
                    url = url.slice(-2);
                    url = 'do/' + module + '/validator/' + url.join('/');
                }
                if (url) {
                    ajaxService = ajaxServiceFactory.buildAjaxService({
                        loadConfig: {
                            'url': url,
                            'method': 'get',
                            'cache': provider.cacheValidationRule
                        }
                    });
                    ajaxService.loadConfig().then(function (response) {
                        var resources = (response && response.data) ? response.data.resources : null;
                        var rule = (response && response.data) ? response.data.rules : null;
                        if (resources) {
                            storageService.setItemToSession(StorageConstants.SESSION_I18N_VALIDATION_RESOURES, resources);
                            $rootScope.$emit(StorageConstants.NOTIFY_CHANGE_LANGUAGE);
                        }
                        defer.resolve(rule);
                    }, function error(error) {
                        provider.log.debug('can not load config[' + url + ']:' + error);
                        defer.reject();
                    });
                } else {
                    defer.resolve({});
                }
                return defer.promise;
            }
            return validatorConfig;
        }
        function preLoadI18nConfig(url, stateName, module) {
            i18nConfig.$inject = ['$rootScope', '$q', 'StorageConstants', 'storageService', 'ajaxServiceFactory'];
            function i18nConfig($rootScope, $q, StorageConstants, storageService, ajaxServiceFactory) {
                var ajaxService, defer = $q.defer();
                if (!url && stateName) {
                    url = stateName.split('_');
                    url = url.slice(-2);
                    url = 'do/' + module + '/resource/' + url.join('/');
                }
                if (url) {
                    ajaxService = ajaxServiceFactory.buildAjaxService({
                        loadConfig: {
                            'url': url,
                            'method': 'get',
                            'cache': provider.cacheResource
                        }
                    });
                    ajaxService.loadConfig().then(function (response) {
                        var resources = response ? response.data : null;
                        if (resources) {
                            storageService.setItemToSession(StorageConstants.SESSION_I18N_RESOURES, resources);
                            $rootScope.$emit(StorageConstants.NOTIFY_CHANGE_LANGUAGE);
                        }
                        defer.resolve(resources);
                    }, function error(error) {
                        provider.log.debug('can not load config[' + url + ']:' + error);
                        defer.reject();
                    });
                } else {
                    defer.resolve({});
                }
                return defer.promise;
            }
            return i18nConfig;
        }
        function preLoadAuthorizationConfig(url, stateName, module) {
            authorizationConfig.$inject = ['$rootScope', '$q', '$stateParams', 'Constants', 'ajaxServiceFactory'];
            function authorizationConfig($rootScope, $q, $stateParams, Constants, ajaxServiceFactory) {
                var ajaxService, defer = $q.defer();
                if (!url && stateName) {
                    url = stateName.split('_');
                    url = url.slice(-2);
                    url = 'do/' + module + '/authorization/' + url.join('/');
                }
                if (url) {
                    ajaxService = ajaxServiceFactory.buildAjaxService({
                        loadConfig: {
                            'url': url,
                            'method': 'get',
                            'cache': provider.cacheAuthorization
                        }
                    });
                    ajaxService.loadConfig().then(function (response) {
                        $stateParams.authorization = buildAuthService($rootScope, Constants, ajaxServiceFactory, response ? response.data : {});
                        defer.resolve($stateParams.authorization);
                    }, function error(error) {
                        provider.log.debug('can not load config[' + url + ']:' + error);
                        defer.reject();
                    });
                } else {
                    defer.resolve({});
                }
                return defer.promise;
            }
            return authorizationConfig;
        }
        function buildAuthService($rootScope, Constants, ajaxServiceFactory, authorizationConfig) {
            var authorizationServices = {}, authService, emptyServiceNames = [];
            angular.forEach(authorizationConfig, function (value, key) {
                if (value == '') {
                    emptyServiceNames.push(key);
                } else {
                    authorizationServices[key] = {
                        url: 'do/' + value,
                        method: 'post',
                        'cache': provider.cacheAuthorization
                    };
                }
            });
            authService = ajaxServiceFactory.buildAjaxService(authorizationServices) || {};
            angular.forEach(emptyServiceNames, function (value, key) {
                authService[value] = angular.noop;
            });
            return authService;
        }
        function isLeafConfig(simpleState) {
            return !simpleState
                || (simpleState.hasOwnProperty('views') || simpleState.hasOwnProperty('templateUrl')
                    || simpleState.hasOwnProperty('__multipleViews') || simpleState.hasOwnProperty('__dependentFiles')
                    || simpleState.hasOwnProperty('__inheritController') || simpleState.hasOwnProperty('__i18nConfig')
                    || simpleState.hasOwnProperty('__layoutConfig') || simpleState.hasOwnProperty('__validatorConfig')
                    || simpleState.hasOwnProperty('__authorizationConfig'));
        }
        function flattenSimpleStateConfig(simpleState) {
            var keys = {
                'views': null,
                'templateUrl': null,
                '__multipleViews': null,
                '__dependentFiles': null,
                '__i18nConfig': null,
                '__layoutConfig': null,
                '__validatorConfig': null,
                '__authorizationConfig': null,
                '__inheritController': null,
                '__ignore': null
            }, config = {}, tempConfig;
            angular.forEach(simpleState, function (value, key) {
                if (isLeafConfig(value)) {
                    config[key] = value || {};
                    if (value && value.__multipleViews) {
                        tempConfig = flattenSimpleStateConfig(value);
                        angular.forEach(tempConfig, function (v, k) {
                            config[key + '.' + k] = v;
                        });
                    }
                } else if (!keys.hasOwnProperty(key)) {
                    if (simpleState.__dependentFiles && simpleState.__dependentFiles.length > 0) {
                        value.__dependentFiles = (value.__dependentFiles || []).concat(simpleState.__dependentFiles);
                    }
                    if (simpleState.params) {
                        value.params = angular.merge((value.params || {}), simpleState.params);
                    }
                    tempConfig = flattenSimpleStateConfig(value);
                    angular.forEach(tempConfig, function (v, k) {
                        if (value.__multipleViews) {
                            config[key + '.' + k] = v;
                        } else {
                            config[key + '_' + k] = v;
                        }
                    });
                }
            });
            return config;
        }
    }
})(angular);